承前例,接著要完成選取學期後 grid 動態查詢更新
前端顯示多筆資料內容像是list、recordset時,一般會使用table(html)、grid這一類元件來顯示。通常這類表格會提供排序和過濾功能,別忘了我們寫的是網頁程式,資料是由後端提供給前端顯示,在提供資料給前端顯示時,一次資料量不宜過多,通常以頁為單位提供資料。
vok-ork 提供的 Data Loaders能達成上述所提到的 sorting、filtering、lazy-loading等功能。vok-ork 提供兩種易於使用的data loaders EntityDataLoader
和SqlDataLoader
。
EntityDataLoader
setDataLoader(Student.dataLoader)
SqlDataLoader
預設情況下,Grid 綁定一個item list,使用setItems()
綁定資料。例:
val students = listOf<Student>(Student(name = "A"),
Student(name = "B"),
Student(name = "C"))
:
:
grid.setItems(students)
例:
data class Student(
override var id: Long? = null,
var name: String? = null,
var birthday: LocalDate? = null,
var created: Date? = null,
var gender: Gender? = null,
var height: Double? = null,
var weight: Double? = null,
var photo: String? = null,
var student_id: String? = null
) : KEntity<Long> {
companion object : Dao<Student, Long>(Student::class.java)
}
grid{
setDataLoader(Student.dataLoader)
}
官方文件中對於 Interface DataProvider
說明如下
A common interface for fetching data from a backend. The DataProvider interface is used by listing components implementing HasDataProvider or HasFilterableDataProvider. The listing component will provide a Query object with request information, and the data provider uses this information to return a stream containing requested beans.
Vaadin comes with a ready-made solution for in-memory data, known as ListDataProvider which can be created using static create methods in this interface. For custom backends such as SQL, EntityManager, REST APIs or SpringData, use a BackEndDataProvider or its subclass.
如前所述,在網頁程式裡,前端資料是由後端提供給前端顯示,grid 動態更改資料來源,不能直接將 List 指定給setItems()
,必需使用ListDataProvider
。接下來的範例中,我們將會使用ListDataProvider
。
此為查詢結果要映射的Bean類,要取得的欄位除了學生成績,還有平均、學生姓名。這個bean類資料要從後端傳到前端,所以必需可序列化,請記得加上 Serializable
data class StudentGrade(
var description: String? = null,
var english: Double? = null,
var math: Double? = null,
var mandarin: Double? = null,
var pe: Double? = null,
var student_id: Long? = null,
var name: String? = null,
var avg: Double? = null
) : Serializable
vok-orm 所提供的 SqlDataLoader所提供的SQL中有幾個比較特別的{{WHERE}}
、{{ORDER}}
、{{PAGING}}
,都是預留空間。當指定條件時會進行替代。
companion object {
val sql = "select grade.*,round((grade.english + grade.math + grade.mandarin + grade.pe)/4,2) as avg, student.name from grade, student where grade.student_id = student.id {{WHERE}} order by 1=1{{ORDER}} {{PAGING}}"
fun getListDataProvider(semester: String?): ListDataProvider<StudentGrade> {
val filter = buildFilter<StudentGrade> {
"grade.description = :semester"("semester" to semester)
}
val provider = SqlDataLoader(DaoOfAny(StudentGrade::class.java), sql)
val result: List<StudentGrade> =
provider.fetch(filter, sortBy = listOf("avg".desc))
return DataProvider.ofCollection(result)
}
}
在此例中,若semester = "第 1 學期",最終 sql 為
select grade.*,round((grade.english + grade.math + grade.mandarin + grade.pe)/4,2) as avg, student.name from grade, student where grade.student_id = student.id and grade.description = `第 1 學期` order by 1=1 , avg desc
對於SQL語法熟悉的開發者而言,應該非常樂見 SqlDataLoader
grid = grid {
isExpand = true
setSizeFull()
dataProvider = StudentGrade.getListDataProvider(null)
addColumn(StudentGrade::name).setHeader("姓名")
addColumn(StudentGrade::description).setHeader("學期")
addColumn(StudentGrade::english).setHeader("英文")
addColumn(StudentGrade::mandarin).setHeader("國文")
addColumn(StudentGrade::math).setHeader("數學")
addColumn(StudentGrade::pe).setHeader("體育")
addColumn(StudentGrade::avg).setHeader("平均")
}
使用 valueChangeListener
來傾聽選項是否改變,若有異動則更新 semester
comboBox<String>("學期") {
setItems(source)
addValueChangeListener {
semester = value
}
}
button("QUERY") {
onLeftClick {
grid.dataProvider = StudentGrade.getListDataProvider(semester)
}
}
上述程式之測試資料為 10000 筆學生資料,每位學生5個學期成績